package hku.cs.lunuo.rank;

import hku.cs.lunuo.Global;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import soot.Body;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.jimple.IdentityStmt;
import soot.jimple.IfStmt;
import soot.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.options.Options;
import soot.toolkits.graph.Block;
import soot.toolkits.graph.ExceptionalBlockGraph;

public class KBCModel3 extends EsBasingModel {

	@Override
	public void analyze() {
		for (String klassName : esStatistic.getKlassNames()) {
			for (String methodName : esStatistic.getMethodNames(klassName)) {
				for (String es : esStatistic.getEss(klassName, methodName)) {
					StatisticProperty sp = esStatistic.getProperty(klassName,
							methodName, es);

					VotedUnit u = new VotedUnit(new EsUnit(klassName,
							methodName, es), sp.tfs);
					VotedGlobal g = new VotedGlobal(esStatistic.totaltf);
					Label label = Global.v().voter.vote(u, g);
					esStatistic.setLabel(klassName, methodName, es, label);

				}
			}
		}
		toBlock();
	}

	private void toBlock() {
		Options.v().set_keep_line_number(true);
		// Options.v().setPhaseOption("jb", "use-original-names:true");
		Scene.v().loadBasicClasses();
		for (String klassName : esStatistic.getKlassNames()) {
			for (String methodName : esStatistic.getMethodNames(klassName)) {
				HashMap<Integer, StatisticProperty> blkData = new HashMap<Integer, StatisticProperty>();

				
//				int min = Integer.MAX_VALUE;
//				for (String es : esStatistic.getEss(klassName, methodName)) {
//					String[] ess = es.split(">");
//					Set<String> ssss = new HashSet<String>();
//					for(int i=0; i<ess.length; i++)
//						ssss.add(ess[i]);
//					
//					for(String s : ssss) {
//						if(min > Integer.parseInt(s))
//							min = Integer.parseInt(s);
//					}
//				}
				
				for (String es : esStatistic.getEss(klassName, methodName)) {

					DefaultLabel ld = (DefaultLabel) (esStatistic.getProperty(
							klassName, methodName, es).label);


					SootClass sc = Scene.v().loadClassAndSupport(klassName);
					SootMethod sm = sc.getMethod(methodName);
					Body body = sm.retrieveActiveBody();
					ExceptionalBlockGraph bGraph = new ExceptionalBlockGraph(
							body);

					Set<Integer> blkSet = new HashSet<Integer>();
					String[] ess = es.split(">");
					int length;
					List<Block> blockLs = bGraph.getBlocks();
					length = ess.length;
					int firstBlkIdx = Integer.parseInt(ess[0]);

					blkSet.add(firstBlkIdx);
					
					
//					for (Block b : blockLs.get(firstBlkIdx).getPreds()) {
//						getConnPreBlock(b, blockLs, blkSet);
//					}

					getConnPreBlock(blockLs.get(firstBlkIdx), blockLs, blkSet);
					
					for (int k = 1; k < length - 1; k++) {
						int nxk = Integer.parseInt(ess[k]);
						blkSet.add(nxk);
					}

					int endBlkIdx = Integer.parseInt(ess[length - 1]);
					if (endBlkIdx == -1) {
						Block b = blockLs
								.get(Integer.parseInt(ess[length - 2]) + 1);
						getConnSucBlock(b, blockLs, blkSet);
					} else {
						blkSet.add(endBlkIdx);
						for (Block b : blockLs.get(endBlkIdx).getSuccs()) {
							if (b.getHead() instanceof IdentityStmt) {
								continue;
							}
							if (b.getIndexInMethod() != endBlkIdx + 1) {
								getConnSucBlock(b, blockLs, blkSet);
								break;
							}
						}
					}
					for (Integer i : blkSet) {
						StatisticProperty sp = blkData.get(i);
						if (sp == null) {
							sp = new StatisticProperty();
						}
						if (sp.label == null) {
							DefaultLabel lwc = new DefaultLabel(
									ld.getGene(), ld.getMainGene());
							sp.label = lwc;
							sp.tfs.add(false);
						} else {
							DefaultLabel lwc = (DefaultLabel) sp.label;

							float xx = lwc.getMinGene()+ld.getMainGene();
							
							if (lwc.compareTo(ld)==-1) {
								lwc.copy(ld);
							}
							lwc.setMinGene(xx);
							sp.label = lwc;
							sp.tfs.add(false);
						}
						blkData.put(i, sp);
					}
				}
				for (Integer i : blkData.keySet()) {
					StatisticProperty sp = blkData.get(i);
					DefaultLabel lwc = (DefaultLabel) sp.label;
//					if(lwc.getTiesLayer()>3)
//						throw new IllegalStateException("ddd");
					lwc.setMinGene(lwc.getMinGene() / sp.tfs.getFlsTimes());
				}

				blkStatistic.setStatisticProperty(klassName, methodName,
						blkData);

			}
		}
	}

//	private void getConnPreBlock(Block b, List<Block> blockLs,
//			Set<Integer> blkSet) {
//		int blkIdx = b.getIndexInMethod();
//		boolean ret = false;
//		blkSet.add(blkIdx);
//		if(b.getPreds().size()!=1)
//			return;
//		else
//			getConnPreBlock(b.getPreds().get(0), blockLs, blkSet);
//	}

	// all the pred and suc block is stop at which block's tail is IF or GOTO.
	// we can also exclude GOTO which may be more accurate
	// but for easy,we omit it

	private void getConnSucBlock(Block b, List<Block> blockLs,
			Set<Integer> blkSet) {
		int blkIdx = b.getIndexInMethod();

		blkSet.add(blkIdx);
		
		if(b.getSuccs().size()!=1)
				return;
		else
			getConnSucBlock(b.getSuccs().get(0), blockLs, blkSet);
			
			
	}
	
	
	private void getConnPreBlock(Block b, List<Block> blockLs,
			Set<Integer> blkSet) {
		int blkIdx = b.getIndexInMethod();
		boolean ret = false;
		Block block = b;

		while (!ret) {
//			if (!block.getTail().branches()) {
			if(!(block.getTail() instanceof IfStmt)) {
				blkSet.add(blkIdx);
				blkIdx--;
				if (blkIdx < 0)
					ret = true;
				else
					block = blockLs.get(blkIdx);
			} else
				ret = true;
		}
	}
//
//	// all the pred and suc block is stop at which block's tail is IF or GOTO.
//	// we can also exclude GOTO which may be more accurate
//	// but for easy,we omit it
//
//	private void getConnSucBlock(Block b, List<Block> blockLs,
//			Set<Integer> blkSet) {
//		int blkIdx = b.getIndexInMethod();
//		boolean ret = false;
//		Block block = b;
//		while (!ret) {
//			blkSet.add(blkIdx);
////			if (block.getTail().branches()
//			if((block.getTail() instanceof IfStmt)
//					|| block.getTail() instanceof ReturnStmt
//					|| block.getTail() instanceof ReturnVoidStmt) {
//				ret = true;
//			} else {
//				blkIdx++;
//				if (blkIdx >= blockLs.size())
//					ret = true;
//				else {
//					block = blockLs.get(blkIdx);
//				}
//			}
//		}
//	}
	
}